(* Author Esther Kundin *)
(* This file has the AST corresponding to the C++ program 
   that we will output that represents the GAQ program that was parse. 
*)

(* c++ binary operators *)
type cop = Add | Sub | Mult | Div | Equal | Neq | Less | Leq | Greater
         | Geq | And | Or

(* c++ expressions *)
type cexpr =
    CLiteral of string
  | CVar of string
  | CStr of string
  | CBincop of cexpr * cop * cexpr
  | CAssign of string * cexpr
  | CNegate of string
  | CNegateId of string
  | CNoexpr

(* output contains either strings or identifiers of variables *)
type atom =
    CId of string
  | CString of string

(* A list of atoms that will be passed to std::cout *)
type cout =
    CStrings of atom list

(* a c++ statement *) 
 
(* cstmt definition *) 
type cstmt =
    CBlock of cstmt list
  | CExpr of cexpr
  | CReturn of cexpr
  | CWhile of cstmt
  | CIf of cexpr * cstmt
  | CElse of cstmt
  | Cout of cout
  | CIn of string
  | Continue
  | Break
  | BreakAll

(* The toplevel representation of a c++ program *)
type cprogram = cstmt

(************************************************************
  Symbol Table 
*************************************************************)
(* need a stringmap to keep track of the declared variables *)
module StringMap = Map.Make(String)

(* Symbol table: Information about all the names in scope *)
type env = {
    (* type of each variable *)
    variable_types : string StringMap.t; 
	(* value as string of each variable*)
    variable_values: string StringMap.t; 
	(* response type in the current scope *)
    response_type: string; 
	(* string value of the response in current scope *)
    response_value: string; 
  }
  
(**************************************************************
  Printing functions for all types that will be used to output 
  the c++ program
***************************************************************)
(* prints out the variables as declarations in the C++ code *)
let print_env env = 
     StringMap.fold
	 (fun key value data ->
	    (Printf.sprintf "%s %s = %s;\n"  value key
	    (StringMap.find key env.variable_values) ) ^ data)
	  env.variable_types ""



(* defines the top-level function for printing a cprogram
   based on the variables already parsed *)
let string_of_cprogram = function
    (statements, myenv) -> 
let string_of_cop = function
    Add -> " + "
  | Sub -> " - "
  | Mult ->  " * "
  | Div -> " / "
  | Equal -> " == "
  | Neq -> " != "
  | Less -> " < "
  | Leq -> " <= "
  | Greater -> " > "
  | Geq -> " >= "
  | And -> " && "
  | Or -> " || "
in

(* 
function get_variable
looks up var name in myenv, return var name if valid 
*)
let get_variable var = 
   if ((String.compare var "response") == 0 ) then 
      "thisresponse" 
   else
     (try StringMap.find var myenv.variable_values; 
	 var with Not_found -> 
	     raise (Failure ("undeclared variable " ^ var)))
in 
(* convert cexpr to a c++ string *)
let rec string_of_cexpr = function
    CLiteral (s) -> s
  | CVar (s) -> get_variable s
  | CStr(s) -> "std::string (" ^ s ^ ")"
  | CBincop(e, o, e2) ->
         "(" ^ string_of_cexpr(e) ^ ") " ^ string_of_cop(o) ^
         " (" ^string_of_cexpr(e2) ^ ")"
  | CAssign (s, e) -> 
      (get_variable s) ^ " = (" ^ string_of_cexpr(e) ^ ")"
  | CNegate (b) -> "-" ^ b
  | CNegateId (id) -> let vbl = get_variable id in "-" ^ vbl 
  | CNoexpr ->  ""
in
(* 
this is the part that prints out the types parsed in from strscanner 
*)
let string_of_printstring = function
   Strast.Variable(s) -> 
      get_variable (String.sub s 1 (String.length(s)-1))
 | Strast.Str(s) -> "\"" ^ s ^ "\""
in
(* here is were we print things out to cout *)
let string_of_atom = function
    CId(s) -> StringMap.find s myenv.variable_values
(* here's the tricky part where we find the $<id> embedded in srings *)
  | CString(s) -> let lexbuf = Lexing.from_string s in 
     Strscanner.toklist.contents <- []; 
	 Strscanner.tokenize lexbuf; 
	 String.concat " << " (List.map string_of_printstring (
	 List.rev Strscanner.toklist.contents) )
in
let string_of_cout = function
  CStrings(s) -> "std::cout << " ^ 
  String.concat " << " (List.map string_of_atom s) ^ " << std::endl;"
in
(* c++ code for all kinds of statements *)
let rec string_of_cstmt = function
    Cout(s) -> string_of_cout(s)
  | CBlock(s) -> String.concat "\n" (List.map string_of_cstmt s)
  | CExpr(e) -> string_of_cexpr(e) ^ ";"  
  | CReturn(e) -> "return (" ^ string_of_cexpr(e) ^ ");\n" 
  | CWhile(s) -> "while (!_break) {\n" ^
                      string_of_cstmt(s)  ^ "\n}\n"
  | CIf (e, s) -> "if (" ^ string_of_cexpr(e) ^ ") {\n" ^
        string_of_cstmt(s) ^ "\n}\n" 
  | CElse (s) -> "else {\n" ^ string_of_cstmt(s) ^ "\n}\n"
  | CIn(t) -> if((String.compare t "string") == 0) then
                 ("std::getline(std::cin, response);\n" ^ "std::string" ^
                  " thisresponse(response);\n" )
              else (ignore(myenv.response_type = t);
                  "std::getline(std::cin, response);\n" ^ t ^
                  " thisresponse;\n" ^
                  "std::istringstream ss(response);\n " ^
                  "if(!(ss >> thisresponse)) {\n" ^
                  "std::cout << \"Invalid input.\" << std::endl; \n " ^
                  "continue;\n}\n")
  | Continue -> "continue;\n"
  | Break -> "break;\n"
    (* use this to break out of all questions. *)
  | BreakAll -> "_break = true;\n"
in
(* 
here is the main body of compile that uses all the above declared 
functions 
*)
        "#include <iostream>\n#include <string>\n" ^
        "#include <sstream>\nusing namespace std;\n int main() {\n" ^
        "std::string response;\n" ^ 
        "std::string thisresponse = \"\";\n" ^ 
        "bool _break = false;\n" ^
        (print_env myenv) ^
        string_of_cstmt (statements) ^
        "\n}\n"

